package com.andrew.idempotent;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.ExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Aspect
@Component
@Slf4j
public class IdempotentAspect {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Autowired
    private RedissonClient redissonClient;
    
    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        // 解析幂等键
        String key = resolveKey(joinPoint, idempotent.key());
        String redisKey = "idempotent:" + key;
        
        // 使用分布式锁，防止并发问题
        RLock lock = redissonClient.getLock(redisKey + ":lock");
        //方法里边使用了分布式锁，这里还需要再使用分布式锁吗， 只用redis来做判断就行了觉得
        try {
            lock.lock(3, TimeUnit.SECONDS);
            
            // 检查是否已处理
            if (redisTemplate.hasKey(redisKey)) {
                log.info("幂等判断，此为重复的请求，key为：{}",key);
                throw new Exception(idempotent.message());
            }
            
            // 设置标记
            redisTemplate.opsForValue().set(redisKey, "processing", 
                                          idempotent.expire(), TimeUnit.SECONDS);
            
            // 执行业务
            Object result = joinPoint.proceed();
            
            // 更新状态为已完成（可选）
            redisTemplate.opsForValue().set(redisKey, "completed", 
                                          idempotent.expire(), TimeUnit.SECONDS);


            log.info("幂等判断，成功请求，key为：{}",key);
            return result;
        }catch (Exception e){
            redisTemplate.delete(key);
            log.info("幂等判断，发生了异常，删除key值，key为：{}",key);
            throw new Exception(e.getMessage());
        } finally {
            if(lock.isLocked() && lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
    
    private String resolveKey(ProceedingJoinPoint joinPoint, String key) {
        Object[] args = joinPoint.getArgs();
        Map<String,Object> mapData = new ConcurrentHashMap<>();
        for(int i=0; i<args.length; i++){
            mapData = BeanUtil.beanToMap(args[i]);
        }

        List<String> keyList = Arrays.stream(key.split(",")).collect(Collectors.toList());
        List<String> valueList = new ArrayList<>();
        for (String k : keyList){
            String value = (String)mapData.getOrDefault(k, null);
            valueList.add(value);
        }

        String value = valueList.stream().collect(Collectors.joining(""));
        log.info("幂等判断，需要获取key为：{}，对应的数值为：{}",key,value);
        return value;
    }
}